/*******************************************************************************
 * Copyright (c) 2009, 2010 SOPERA GmbH
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/

import java.util.ArrayList
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.util.Date
import java.util.Random
import java.util.regex.Pattern

import org.hyperic.hibernate.Util
import org.hyperic.hibernate.SortField

import org.hyperic.hq.hqu.rendit.BaseController
import org.hyperic.hq.hqu.rendit.html.DojoUtil
import org.hyperic.hq.hqu.rendit.html.HtmlUtil
import org.hyperic.hq.hqu.rendit.util.HQUtil
import org.hyperic.hq.hqu.rendit.helpers.ResourceHelper
import org.hyperic.hq.hqu.rendit.helpers.MetricHelper

import org.hyperic.hq.appdef.Ip
import org.hyperic.hq.appdef.server.session.Server
import org.hyperic.hq.appdef.server.session.Service
import org.hyperic.hq.appdef.server.session.ServiceType
import org.hyperic.hq.appdef.server.session.ServerManagerEJBImpl
import org.hyperic.hq.appdef.server.session.ServiceManagerEJBImpl

import org.hyperic.hq.measurement.shared.DataManagerLocal
import org.hyperic.hq.measurement.server.session.DataManagerEJBImpl
import org.hyperic.hq.measurement.server.session.Measurement
import org.hyperic.hq.authz.shared.AuthzConstants
import org.hyperic.util.pager.PageControl

import groovy.xml.MarkupBuilder

import SopMssField
import MssMessage

class SamController 
extends BaseController {
	// Columns of SOPERA Messages table
	private static SopMssField fId 			= new SopMssField("ID", "ID")
	private static SopMssField fSbbId 		= new SopMssField("SBB ID", "SBB_ID")
	private static SopMssField fAppId 		= new SopMssField("App ID", "APP_ID")
	private static SopMssField fMsgTypeId 	= new SopMssField("Msg type ID", "MSG_TYPE_ID")
	private static SopMssField fLevel 		= new SopMssField("Level", "LEVL")
	private static SopMssField fCategory 	= new SopMssField("Category", "CATEGORY")
	private static SopMssField fText 		= new SopMssField("Description", "TEXT")
	private static SopMssField fDateTime 	= new SopMssField("Date/Time", "TIMESTAMP")

	private int fPageCount = 0	// Count of pages returned by current request
	
	private ServiceMan = ServiceManagerEJBImpl.getOne()
	private ServerMan = ServerManagerEJBImpl.getOne()
	
	/**
	 * Gets parameter of request and makes it safe
	 * @param params - Request parameters
	 * @param name - Name of parameter
	 * @return Safe value of parameter
	 */
	private def getParam(params, name) {
		def p = params.getOne(name);
		if (p == null || p == '' || p == "undefined") {
			return '';
		}
		return p.replaceAll("'", "\\\\'");
	}

	/**
	 * This function is called to update data in dojoTable
	 * @param pageInfo - Contains information about page, like page number or page size
	 * @param params - Parameters of request
	 * @return List of MssMessages
	 */
	private updateMssData(pageInfo, params) {
		String fltrLevel = getParam(params, 'fltrLevel');
		String fltrCategory = getParam(params, 'fltrCategory');
		String fltrDateFrom = getParam(params, 'fltrDateFrom');
		String fltrDateTo = getParam(params, 'fltrDateTo');
		String fltrDescription = getParam(params, 'fltrDescription');
		String fltrSbbId = getParam(params, 'fltrSbbId');
		String fltrAppId = getParam(params, 'fltrAppId');
		String fltrMsgtypeID = getParam(params, 'fltrMsgTypeId');
		String appId = getParam(params, 'appId');
		
		if (!appId)
			appId = fltrAppId;
		
		if (!fltrDateFrom)
			fltrDateFrom = fltrDateTo;
		else if (!fltrDateTo)
			fltrDateTo = fltrDateFrom;

		String sortColumn = pageInfo.getSort().getColumn();
		String sortDirection = pageInfo.isAscending() ? "DESC" : "ASC";
		
		int pageSize = pageInfo.getPageSize();
		int startRow = pageInfo.getStartRow();
		
		boolean isWhereNeeded = appId || fltrSbbId || fltrMsgtypeID || fltrLevel || fltrCategory || fltrDescription || fltrDateFrom;
		String Dlct = "";
		Dlct = Util.getDialect().toString();	
		String sqlSelect = "SELECT `ID`,`SBB_ID`,`APP_ID`,`MSG_TYPE_ID`,`LEVL`,`CATEGORY`,`TEXT`,`TIMESTAMP` ";
		if(Dlct.indexOf("ostgr") >= 0)	{		
			sqlSelect = "SELECT ID,SBB_ID,APP_ID,MSG_TYPE_ID,LEVL,CATEGORY,TEXT,TIMESTAMP ";
		}
		String sqlCount = "SELECT COUNT(`ID`) ";
		if(Dlct.indexOf("ostgr") >= 0)	{
			sqlCount = "SELECT COUNT(ID) ";
		}
		String sqlFromMessages = "" +
				"FROM MESSAGES " + (isWhereNeeded ? "WHERE 1 " : '') +
				(appId ? " AND `APP_ID` LIKE '%${appId}%' " : '') +
				(fltrSbbId ? " AND `SBB_ID` LIKE '%${fltrSbbId}%' " : '') +
				(fltrMsgtypeID ? " AND `MSG_TYPE_ID` LIKE '%${fltrMsgtypeID}%' " : '') +
				(fltrLevel ? " AND `LEVL` LIKE '%${fltrLevel}%' " : '') +
				(fltrCategory ? " AND `CATEGORY` LIKE '%${fltrCategory}%' " : '') +
				(fltrDescription ? " AND `TEXT` LIKE '%${fltrDescription}%' " : '') +
				(fltrDateFrom ? "AND DATE(`TIMESTAMP`) BETWEEN '${fltrDateFrom}' AND '${fltrDateTo}' " : '') +
				"ORDER BY `${sortColumn}` ${sortDirection}";
		if(Dlct.indexOf("ostgr") >= 0)	{
			sqlFromMessages = "" +
			"FROM MESSAGES " + (isWhereNeeded ? "WHERE 1 " : '') +
			(appId ? " AND APP_ID LIKE '%${appId}%' " : '') +
			(fltrSbbId ? " AND SBB_ID LIKE '%${fltrSbbId}%' " : '') +
			(fltrMsgtypeID ? " AND MSG_TYPE_ID LIKE '%${fltrMsgtypeID}%' " : '') +
			(fltrLevel ? " AND LEVL LIKE '%${fltrLevel}%' " : '') +
			(fltrCategory ? " AND CATEGORY LIKE '%${fltrCategory}%' " : '') +
			(fltrDescription ? " AND TEXT LIKE '%${fltrDescription}%' " : '') +
			(fltrDateFrom ? "AND DATE(TIMESTAMP) BETWEEN '${fltrDateFrom}' AND '${fltrDateTo}' " : '') +
			"ORDER BY ${sortColumn} ${sortDirection}";
		}
		PreparedStatement query = Util.getConnection().prepareStatement(sqlCount + sqlFromMessages);
		ResultSet res = query.executeQuery();
		if (pageSize == 0)
			fPageCount = 0;
		else if (res != null && res.next())
			fPageCount = Math.ceil(res.getInt(1) / pageSize);

		if (fPageCount > 0) {
			int lastStartRow = (fPageCount - 1) * pageSize;
			if (startRow > lastStartRow)
				startRow = lastStartRow;
		}
		
		String sqlLimoff = " LIMIT ${pageSize} OFFSET ${startRow}";
		if(Dlct.indexOf("ostgr") >= 0)	{
			sqlLimoff = " LIMIT ${pageSize} OFFSET ${startRow}";
		}
		query = Util.getConnection().prepareStatement(sqlSelect + sqlFromMessages + sqlLimoff);
		res = query.executeQuery();
		
		List<MssMessage> m_items = new ArrayList<MssMessage>();
		
		if (res != null) {
			while (res.next()) {
				MssMessage msg = new MssMessage();
				
				msg.setId(res.getLong(1));
				msg.setSbbId(res.getString(2));
				msg.setAppId(res.getString(3));
				msg.setMsgTypeId(res.getString(4));
				msg.setLevel(res.getString(5));
				msg.setCategory(res.getString(6));
				msg.setText(res.getString(7));
				msg.setTimestamp(res.getTimestamp(8));
				
				m_items.add msg
			}
		}		
		return m_items;
	}
	
	/**
	 * Returns list of distinct values from specified column in table
	 * @param fieldName - Name of column in table
	 * @return List of distinc values in column
	 */
	private ArrayList<String> getFieldList(String fieldName) {
		String Dlct = "";
		Dlct = Util.getDialect().toString();
		String sql = "SELECT DISTINCT `${fieldName}` FROM MESSAGES ORDER BY `${fieldName}`";
		if(Dlct.indexOf("ostgr") >= 0)	{
			sql = "SELECT DISTINCT ${fieldName} FROM MESSAGES ORDER BY ${fieldName}";
		}
		PreparedStatement query = Util.getConnection().prepareStatement(sql);
		ResultSet res = query.executeQuery();
		ArrayList<String> arr = new ArrayList<String>();
		
		if (res != null) {
			while (res.next()) {
				arr.add res.getString(1)
			}
		}
		
		return arr;
	}
	
	/**
	 * Describes dojoTable view
	 */
	private SOP_SHEMA =  [
	getData: {pageInfo, params -> updateMssData(pageInfo, params);
	},
	defaultSort: fId,
	defaultSortOrder: 0,
	columns: [
	[field:fId, label:{it.getId()
	}, width:'0px'],
	[field:fLevel, label:{it.getLevel()
	}],
	
	[field:fCategory, label:{it.getCategory()
	}],
	
	[field:fDateTime, label:{it.getTimestamp()
	}],
	
	[field:fText, label:{it.getText()
	}],
	
	[field:fSbbId, label:{it.getSbbId()
	}],
	
	[field:fAppId, label:{it.getAppId()
	}],
	
	[field:fMsgTypeId, label:{it.getMsgTypeId()
	}]
	]
	]
	
	protected void init() {
		onlyAllowSuperUsers()
		setJSONMethods(['getTableData'])
	}
	
	def index(params) {
		def sbbIdList = null;
		def appIdList = null;
		def levelList = null;
		def categoryList = null;
		
		try {
			sbbIdList = getFieldList(fSbbId.getColumn());
			appIdList = getFieldList(fAppId.getColumn());
			levelList = getFieldList(fLevel.getColumn());
			categoryList = getFieldList(fCategory.getColumn());
		} catch (Exception) {
		}
		
		render(locals:[ sopMsgShema : SOP_SHEMA, msgTableId: 'MsgTable',
		                listSbbId: sbbIdList, listAppId: appIdList,
		                listLevel: levelList, listCategory: categoryList ])
	}
	
	/**
	 * This method is called when dojoTable needs to update
	 * @param params - Parameters of request
	 * @return json with data
	 */
	def getTableData(params) {
		def resJson = DojoUtil.processTableRequest(SOP_SHEMA, params)
		resJson.put("pageCount", "${fPageCount}");
		def pgNum = resJson.optInt("pageNum");
		resJson.put("pageNum", pgNum > fPageCount ? fPageCount-1 : pgNum);
	}
	
	def getTreeData(params) {
		def resourceType = 'SOPERA'
		//def resourceType = 'JBoss 4.2'
		def json = " {\n" + 
				" \"identifier\" : \"nodeID\",\n" +
				" \"label\" : \"name\",\n" +
				" \"items\" :\n" +
				" [\n"
		try {
			def overlord = HQUtil.overlord
			ResourceHelper rhelp = new ResourceHelper(overlord)
			def prototype = rhelp.findResourcePrototype(resourceType)
			def resources = rhelp.findResourcesOfType(prototype)
			Server srv = null
			Service svc
			Integer resType
			Integer aeid
			
			int i = 0
			
			///////////////////////////
			json += " \t{\n" +
			" \t\"name\" : \"SOPERA Servers\",\n" +
			" \t\"type\" : \"root\",\n" + 
			" \t\"nodeID\" : \"root\",\n"+ 
			" \t\"children\" :\n" +
			" \t\t[\n" 
			///////////////////////////
			
			for (r in resources) {
				if(i > 0) json += ", \n"
				
				String rname = r.name
				resType = r.getResourceType().getId() 
				aeid = r.getInstanceId()
				
				if (resType.equals(AuthzConstants.authzServer)) {
					srv = ServerMan.findServerById(aeid)
				}
				
				def availability = getResAvailability(r.getId())
				def typeS = "serverGray"
				
				if (availability > 0 && availability <= 1)
					if (availability <= 0.5)
						typeS = "serverYellow"
					else typeS = "serverGreen"
				else 
				if(availability == 0) 
					typeS = "serverRed"
				
				json += " \t\t{\n" +
						//" \t\t\"name\" : \"${r.name} (IP:${srv.getPlatform().getIps().get(0).getAddress()})\",\n" +
						" \t\t\"name\" : \"${r.name}\",\n" +
						" \t\t\"nodeID\" : \"0${i}\",\n"+ 
						" \t\t\"type\" : \"" + typeS + "\",\n" + 
						" \t\t\"eid\" : \"" + aeid + "\",\n" +
						" \t\t\"category\" : \"2\",\n" +
						" \t\t\"children\" :\n" +
						" \t\t\t[\n" 
				int j = 0;
				for (serviceType in ServiceMan.getServiceTypesByServerType(getUser(), srv.getServerType().getId())) {
					aeid = serviceType.getId()
					if(j > 0) json += ", \n"
					String sname = serviceType.name
					ServiceType stp = ServiceMan.findServiceType(serviceType.getId())
					
					
					// now the availability of all serviceType is equals the server availability  
					typeS = "serviceGray"
					availability = getResAvailability(r.getId())
					if (availability > 0 && availability <= 1) 
						if (availability <= 0.5)
							typeS = "serviceYellow"
						else typeS = "serviceGreen" 
					else 
					if(availability==0) 
						typeS = "serviceRed"
					
					json += " \t\t\t\t{\n" +
							"\t\t\t\t\t\"nodeID\" : \"1${i}${j}\",\n"+ 
							"\t\t\t\t\t\"name\" : \"${sname}\",\n"+ 
							"\t\t\t\t\t\"type\" : \""+typeS+"\",\n" +
							"\t\t\t\t\t\"eid\" : \"" + r.getInstanceId() + "\",\n" +
							"\t\t\t\t\t\"ctype\" : \"" + serviceType.getId() + "\",\n" +
							"\t\t\t\t\t\"category\" : \"2\",\n" +
							"\t\t\t\t\t\"ctypecategory\" : \"3\""
					int k = 0
					
					boolean needAppId = false;
					if (stp.getName().equals('SOPERA Participant') || stp.getName().equals('SOPERA Service'))
						needAppId = true;
					
					for (service in ServiceMan.findServicesByType(srv,stp)) {
						if(k > 0) json += ", \n"
						else {
							json += ",\n \t\t\t\t\t\"children\" :\n" +
									" \t\t\t\t\t[\n"
						}
						sname = service.name
						sname = sname.replace(rname + " ", "")
						svc = ServiceMan.findServiceById(service.getId())
						availability = getResAvailability(svc.resource.id)
						typeS = "serviceGray"
						if (availability > 0 && availability <= 1) 
							if (availability <= 0.5)
								typeS = "serviceYellow"
							else typeS = "serviceGreen" 
						else 
						if(availability==0) 
							typeS = "serviceRed"
							
						def appid = needAppId ? sname : '';
							
						json += " \t\t\t\t\t\t{\n" +
								" \t\t\t\t\t\t\t\"name\" : \"${sname}\",\n"+ 
								" \t\t\t\t\t\t\t\"nodeID\" : \"2${i}${j}${k}\",\n"+ 
								" \t\t\t\t\t\t\t\"sopType\" : \"${serviceType.name}\",\n"+ 
								" \t\t\t\t\t\t\t\"type\" : \""+typeS+"\",\n" + 
								" \t\t\t\t\t\t\t\"eid\" : \"" + service.getId() + "\",\n" +
								" \t\t\t\t\t\t\t\"category\" : \"3\",\n" +
								" \t\t\t\t\t\t\t\"appid\" : \"${appid}\"\n" 
						json +=	" \t\t\t\t\t\t}" 
						k++
					}
					if(k > 0) json += "\n\t\t\t\t\t]" else json += "\n"
					json +=	"\n\t\t\t\t}" 
					j++
				}
				json += "\n\t\t\t]" +
						" \n\t\t}"
				i++
			}
			json += " \t\n\t\t]\n" + " \t}\n"
			json += " ]\n" + " }\n"
		}
		
		catch(Exception e) {
			json = e.getMessage()
		}
		render(inline:"/* ${json} */", contentType: 'text/plain')  
	}
	
	
	def getMetricsAsXML(params) {
		def overlord = HQUtil.overlord
		def rhelp = new ResourceHelper(overlord)
		def _services = rhelp.findAllServices()
		def _servers = rhelp.findAllServers()
		
		def writer = new StringWriter()
		def xml = new MarkupBuilder(writer)
		
		xml.metrics {
			services() {
				for (s in _services) {
					service(name: s.name) {
						for (m in s.enabledMetrics) {
							def metricData = m.lastDataPoint
							if (metricData != null) {
								metric(name: m.template.name,
										value: metricData.value,
										time: metricData.timestamp,
										id: m.id,
										idI: m.instanceId)
							}
						}
					}
				}
			}
			
			servers() {
				for (s in _servers) {
					server(name: s.name) {
						for (m in s.enabledMetrics) {
							def metricData = m.lastDataPoint
							if (metricData != null) {
								metric(name: m.template.name,
										value: metricData.value,
										time: metricData.timestamp,
										id: m.id,
										idI: m.instanceId)
							}
						}
					}
				}
			}
		}
		
		render(inline:"/* ${writer.toString()} */", contentType: 'text/plain')
		
	}
	
	private float getResAvailability(int rid){
		int out = -1
		try {
			
			def overlord = HQUtil.overlord
			ResourceHelper rhelp = new ResourceHelper(overlord)
			def res = rhelp.findById(rid)
			
			for (m in res.enabledMetrics) {
				if("Availability".equals(m.template.name.toString())){
					def metricData = m.lastDataPoint
					if (metricData != null) {
						out = metricData.value
					}
				}
			}
		}
		catch (Exception e) {
			out = -1
		}
		return out
	}	
	
	private Measurement getMetricFromRes(int rid){
		
		int out = -1
		def aeid
		def srv = null
		def svc = null
		
		try {
			def overlord     = HQUtil.overlord
			ResourceHelper rhelp        = new ResourceHelper(overlord)
			def res    = rhelp.findById(rid)
			try {
				srv = ServerManagerEJBImpl.getOne().findServerById(rid)
			}
			catch (Exception e) {
				srv = null
			}
			if (srv != null) {
				for (m in srv.resource.enabledMetrics) {
					if("Availability".equals(m.template.name.toString())){
						return m
					}
				}
			}
			try {
				svc = ServiceManagerEJBImpl.getOne().findServiceById(rid)
			}
			catch (Exception e) {
				svc = null
			}
			if (svc != null) {
				for (m in svc.resource.enabledMetrics) {
					if("Availability".equals(m.template.name.toString())){
						return m
					}
				}
			}
		}
		catch (Exception e) {
			return null
		}
		return null
		
	}
	
	def  getMetricData(params) {
		
		def overlord = HQUtil.overlord
		def mhelp = new MetricHelper(overlord)
		
		def metricId     // the metric ID from params
		def	metric       // the metric
		def start        // 
		def end          //
		def data         // the List of metrics from start to end
		Date d           // timestamp from datapoint as Date
		final int INTERVAL = 8 // count of minutes in the interval
		int intv 
		int rid
		
		def json = "{ \"values\" : [\r\n" 
		
		try {
			end = System.currentTimeMillis()
			start = end - (INTERVAL * 60 * 60 * 1000)
			intv = INTERVAL * 60 * 1000
			//metricId = params.getOne("mid")?.toInteger()
			rid = params.getOne("rid")?.toInteger()
			metric = getMetricFromRes(rid)
			DataManagerLocal dMan = DataManagerEJBImpl.getOne();
			data = dMan.getHistoricalData([metric], start, end,
					intv, 0, true,
					PageControl.PAGE_ALL)
			
			for (dp in data) {
				d = new java.util.Date(dp.timestamp)
				json += "\t{ \"dt\" : \"" + d + "\", \"m\" : \"" + dp.value + "\" },\r\n" 
			}
			json += "] }"
		}
		
		catch (Exception e) {
			render(inline:"/* ${e.getMessage()} */", contentType: 'text/plain')
			
		}
		render(inline:"/* ${json} */", contentType: 'text/plain')
	}	
	
	def  getMetricDataXML(params) {
		
		def overlord = HQUtil.overlord
		def mhelp = new MetricHelper(overlord)
		
		def writer = new StringWriter()
		def xml = new MarkupBuilder(writer)
		Measurement	metric
		def start 
		def end 
		def metricId
		def intv
		def rid
		Date d
		
		try {
			DataManagerLocal dMan = DataManagerEJBImpl.getOne();
			end = System.currentTimeMillis()
			start = end - (8 * 60 * 60 * 1000)
			intv = 8 * 60 * 1000
			
			//metricId = params.getOne("mid")?.toInteger()
			rid = params.getOne("rid")?.toInteger()
			metricId = getMetricIdFromRes(rid)
			metric = getMetricFromRes(rid)
			data = metric.getData(end - (8 * 1 * 60 * 1000), end)
			List data = dMan.getHistoricalData([metric], start, end,
					intv, 0, true,
					PageControl.PAGE_ALL)
			xml.metrics {
				servers(rid:rid) {
					metricdata(resourceId: metric.resource.id, 
					resourceName: metric.resource.name,
					metricId: metric.id,
					metricName: metric.template.name) {
						for (dp in data) {
							d = new java.util.Date(dp.timestamp)
							DataPoint(
									date : d,
									value : dp.value)
						}
					}
				}
			}
		}
		catch (Exception e) {
			render(inline:"/* ${e.getMessage()} */", contentType: 'text/plain')
			
		}
		render(inline:"/* ${writer.toString()} */", contentType: 'text/plain')
	}	
}
